package com.gotobject.junit.runners;

import static com.gotobject.filter.common.Iterables.asList;
import static com.gotobject.junit.internal.runners.conditions.ConditionMaker.*;
import com.gotobject.junit.internal.runners.model.Explosion;
import com.gotobject.junit.internal.runners.model.PerformanceKey;
import com.gotobject.junit.internal.runners.statements.FailOnExplosionShell;
import org.junit.Test;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;

import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * a typical scenario looks like this:
 *
 * <pre>
 *  &#064;RunWith(ConditionalPerfJUnitClassRunner.class)
 *  public class SampleThroughputPerfTest {
 *
 *      &#064;ConfigureWith
 *      public static Object[][] getPerformanceTestingData(){
 *          return new Object[][]{
 *              new Object[]{2, 2},
 *              new Object[]{10, 10},
 *          };
 *      }
 *
 *      &#064;Throughput("getPerformanceTestingData")
 *      &#064;Test(timeout = 70000)
 *      &#064;If(condition = "isDBConnectionOk")
 *      public void testSomething() throws Exception {
 *          // ... some code goes here
 *      }
 *
 *      public static boolean isDBConnectionOk(){
 *          boolean result = false;
 *          // ... some processing goes here to check if there db connection
 *          // ... then assign it to result.
 *          return result;
 *      }
 * }
 * </pre>
 *
 * @author Huascar A. Sanchez (huascar.sanchez@gmail.com)
 */
public class ConditionalJUnitPerfClassRunner extends ConditionalJUnitClassRunner {
    private final List<Explosion> inAutocruised;

    /**
	 * Creates a {@code ConditionalJUnitPerfClassRunner} to run {@code klass}
	 *
	 * @param klass
	 * 		a class to be run.
	 * @throws
     *      org.junit.runners.model.InitializationError
	 *      if the test class is malformed.
	 */
	public ConditionalJUnitPerfClassRunner(Class<?> klass) throws InitializationError {
        this(klass, new ArrayList<Explosion>());
	}

    /**
	 * Creates a {@code ConditionalJUnitPerfClassRunner} to run {@code klass}
     *
     * @param klass to be run.
     * @param config
     *      explosion configuration.
     * @throws InitializationError
     *      if the test class is malformed.
     */
    public ConditionalJUnitPerfClassRunner(Class<?> klass, Iterable<Explosion> config) throws InitializationError {
        super(klass);
        inAutocruised = new CopyOnWriteArrayList<Explosion>(asList(config));
    }


    protected Statement withPerformanceTestsInXml(PerformanceKey nakedLiteral, FrameworkMethod method, Statement next){
		final FailOnExplosionShell explosionShell = new FailOnExplosionShell.Builder(inAutocruised)
			.root(getTestClass())
			.next(next)
			.notifier(getRunNotifier())
			.description(describeChild(method))
			.configuredMethod(nakedLiteral.getConfigName())
			.perfType(nakedLiteral)
			.build();
		return explosionShell.newStatement();
    }
	

	protected Statement withPerformanceTestsManuallyDefined(PerformanceKey perfType, FrameworkMethod method, Statement next){
           final FailOnExplosionShell explosionShell = new FailOnExplosionShell.Builder(inAutocruised)
                   .root(getTestClass())
                   .next(next)
                   .notifier(getRunNotifier())
                   .description(describeChild(method))
                   .configuredMethod(perfType.getConfigName())
                   .perfType(perfType)
                   .timeout(annotationFor(method, Test.class).timeout())
               .build();
           return explosionShell.newStatement();
    }

    @Override
    protected Statement withPotentialTimeout(FrameworkMethod method, Object test, Statement next) {
        final PerformanceKey     perfType           = PerformanceKey.from(method);

		if(skippedAll(method, inAutocruised)){
            return super.withPotentialTimeout(method, test, next);
        }

       if(autocruised(method, inAutocruised)){
           Statement xmlExplosions = null;
           for(Explosion each : inAutocruised){
               final PerformanceKey nakedLiteral   = PerformanceKey.like(each.getTestType());
               xmlExplosions = likelyManualExplosion(each)
                             ? super.withPotentialTimeout(method, test, next)
                             : withPerformanceTestsInXml(nakedLiteral, method, next);
           }
           return xmlExplosions;
       } else {
           return withPerformanceTestsManuallyDefined(perfType, method, next);
       }
    }
}
